//@author A0085897A

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

public class Database {

	// ATTRIBUTES
	private List<Transaction> _transactions;
	private List<String> _categories, _paymentMethods;
	private String _currency;

	// CONSTANTS
	protected final static String DATABASE_FOLDER = "database";
	protected final static String DATABASE_PATH = System.getProperty("user.dir") + "/" + DATABASE_FOLDER + "/";
	protected final static String TRANSACTIONS_FILENAME = DATABASE_PATH + "transactions.xml";
	protected final static String CATEGORIES_FILENAME = DATABASE_PATH + "categories.xml";
	protected final static String PAYMENT_METHODS_FILENAME = DATABASE_PATH + "payment_methods.xml";

	// CONSTRUCTOR
	public Database() {
		this.load();
	}

	// METHODS
	// Getters
	public List<Transaction> getTransactions() {
		return this._transactions;
	}
	public List<String> getCategories() {
		return this._categories;
	}
	public List<String> getPaymentMethods() {
		return this._paymentMethods;
	}
	public String getCurrency() {
		return this._currency;
	}
	// Setters
	private void setTransactions(List<Transaction> transactions) {
		this._transactions = transactions;
	}
	private void setCategories(List<String> categories) {
		this._categories = categories;
	}
	private void setPaymentMethods(List<String> paymentMethods) {
		this._paymentMethods = paymentMethods;
	}
	private void setCurrency(String currency) {
		this._currency = currency;
	}
	// Load from database
	public void load() {
		this.setTransactions(new ArrayList<Transaction>());
		this.setCategories(new ArrayList<String>());
		this.setPaymentMethods(new ArrayList<String>());
		this.setCurrency(Account.DEFAULT_CURRENCY);
		File folder = new File(DATABASE_FOLDER);
		if (folder.isDirectory()) {
			this.loadTransactions();
			this.loadTaxonomy(this.getCategories(), CATEGORIES_FILENAME);
			this.loadTaxonomy(this.getPaymentMethods(), PAYMENT_METHODS_FILENAME);
		} else {
			folder.mkdir();
		}
	}
	// Load transactions
	@SuppressWarnings("unchecked")
	private void loadTransactions() {
		SAXReader saxReader = new SAXReader();
		File file = new File(TRANSACTIONS_FILENAME);
		if (file.exists()) {
			try {
				Document document = saxReader.read(file);
				Element root = document.getRootElement();
				this.setCurrency(root.attributeValue("currency"));
				Iterator<Element> iterator = root.elementIterator();
				while (iterator.hasNext()) {
					Element transactionNode = iterator.next();
					this.getTransactions().add(
							new Transaction(
									transactionNode.elementText("name"),
									Double.parseDouble(transactionNode.elementText("value")),
									transactionNode.elementText("type"),
									transactionNode.elementText("date"),
									transactionNode.elementText("category"),
									transactionNode.elementText("paymentMethod"),
									transactionNode.elementText("remarks"),
									transactionNode.elementText("status")	
									)
							);
				}
			} catch (DocumentException e) {
				e.printStackTrace();
			} catch (NumberFormatException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	// Load taxonomy (category or payment method)
	@SuppressWarnings("unchecked")
	private void loadTaxonomy(List<String> list, String filename) {
		SAXReader saxReader = new SAXReader();
		File file = new File(filename);
		if (file.exists()) {	
			try {
				Document document = saxReader.read(file);
				Element root = document.getRootElement();
				Iterator<Element> iterator = root.elementIterator();
				while (iterator.hasNext()) {
					String str = iterator.next().getText();
					list.add(str);
				}
			} catch (DocumentException e) {
				e.printStackTrace();
			}
		}
	}
	// Save to database
	public void save(List<Transaction> transactions, List<String> categories, List<String> paymentMethods, String currency) {
		this.setTransactions(transactions);
		this.setCategories(categories);
		this.setPaymentMethods(paymentMethods);
		this.setCurrency(currency);
		File folder = new File(DATABASE_FOLDER);
		if (!folder.isDirectory()) {
			folder.mkdir();
		}
		this.saveTransactions();
		this.saveTaxonomy(this.getCategories(), "categories", "category", CATEGORIES_FILENAME);
		this.saveTaxonomy(this.getPaymentMethods(), "paymentMethods", "paymentMethod", PAYMENT_METHODS_FILENAME);
	}
	// Save transactions
	private void saveTransactions() {
		if (this.getTransactions().size() != 0) {
			Document document = DocumentHelper.createDocument();
			Element root = document.addElement("transactions").addAttribute("currency", this.getCurrency());
			for (Transaction transaction: this.getTransactions()) {
				Element transactionNode = root.addElement("transaction");
				transactionNode.addElement("name").addText(transaction.getName());
				transactionNode.addElement("value").addText(transaction.getValue() + "");
				transactionNode.addElement("type").addText(transaction.getType().toString());
				transactionNode.addElement("date").addText(transaction.getDateStr());
				transactionNode.addElement("category").addText(transaction.getCategory());
				transactionNode.addElement("paymentMethod").addText(transaction.getPaymentMethod());
				transactionNode.addElement("remarks").addText(transaction.getRemarks());
				transactionNode.addElement("status").addText(transaction.getStatus().toString());
			}
			this.write(document, TRANSACTIONS_FILENAME);
		}
	}
	// Save taxonomy (category or payment method)
	private void saveTaxonomy(List<String> list, String rootName, String nodeName, String filename) {
		if (list.size() != 0) {
			Document document = DocumentHelper.createDocument();
			Element root = document.addElement(rootName);
			for (String str: list) {
				root.addElement(nodeName).addText(str);
			}
			this.write(document, filename);
		}
	}
	// Write to an XML file
	private void write(Document document, String filename) {
		try {
			XMLWriter writer;
			writer = new XMLWriter(new FileWriter(filename), new OutputFormat("\t", true));
			writer.write(document);
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	// Delete entire database
	public void reset() {
		try {
			File[] files = new File[]{new File(TRANSACTIONS_FILENAME), new File(CATEGORIES_FILENAME), new File(PAYMENT_METHODS_FILENAME)};
			for (File file: files) {
				if (file.exists()) {
					file.delete();
				}
			}
		} catch (SecurityException e){
			e.printStackTrace();
		}
	}

}
